package com.autonavi.minimap.threadpool;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class DDAIThreadPool implements IDThreadPool {
	ThreadPoolExecutor me;
	CommandFactory mcf;
	PriorityBlockingQueue<Runnable> q;
	HashMap<String, IPriorityTask> taskManager = new HashMap<String, IPriorityTask>();
	
	/**
	 * Lock used for all public operations
	 */
	private final ReentrantLock lock;
	

	public static IDThreadPool newFixedThreadPool(int coreCount,
			boolean sortByLatest) {
		return new DDAIThreadPool(coreCount, coreCount, 0, sortByLatest);
	}

	public static IDThreadPool newCachedThreadPool(boolean sortByLatest) {
		return new DDAIThreadPool(0, Integer.MAX_VALUE, 10000, sortByLatest);
	}

	public static IDThreadPool newFixedThreadPool(int coreCount) {
		return new DDAIThreadPool(coreCount, coreCount, 0);
	}

	public static IDThreadPool newCachedThreadPool() {
		return new DDAIThreadPool(0, Integer.MAX_VALUE, 10000);
	}

	private DDAIThreadPool(int corePoolSize, int maximumPoolSize,
			long keepAliveTime, boolean sortByLatest) {
		q = new PriorityBlockingQueue<Runnable>();
		me = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
				keepAliveTime, TimeUnit.MILLISECONDS, q,
				new PriorityThreadFactory("thread-pool", 10));
		mcf = new CommandFactory(sortByLatest);
		lock = new ReentrantLock();
	}

	private DDAIThreadPool(int corePoolSize, int maximumPoolSize,
			long keepAliveTime) {
		q = new PriorityBlockingQueue<Runnable>();
		me = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
				keepAliveTime, TimeUnit.MILLISECONDS, q,
				new PriorityThreadFactory("thread-pool", 10));
		mcf = new CommandFactory();
		lock = new ReentrantLock();
	}

	ITaskHandler th = new ITaskHandler() {

		@Override
		public void onFinish(String flag) {
			lock.lock();
			try {
				if (taskManager.containsKey(flag)) {
					taskManager.remove(flag);
				}
			} finally {
				lock.unlock();
			}
		}
	};

	private void execute(String category, IPriorityTask runnable, int priority) {
		if (runnable != null) {
			me.execute(mcf.getTask(category, runnable, priority, th));
		}
	}

	@Override
	public void shutdownNow() {
		me.shutdownNow();
	}


	public void put(String category, IPriorityTask runnable) {
		put(category, runnable, IPriorityTask.PRIORITY_MEDIUM);

	}

	public void put(String category, IPriorityTask runnable, int priority) {
		if(runnable==null){
			return;
		}
		String key = runnable.getFlag();
		lock.lock();
		try{
			if (taskManager.containsKey(key)) {
				if (!taskManager.get(key).onRepeatPut(runnable)) {
					runnable.isolateFlag();
//					taskManager.put(runnable.getFlag(), runnable);
//					execute(category, runnable, priority);
					put(category, runnable, priority);
				}
			}else{
				taskManager.put(runnable.getFlag(), runnable);
				execute(category, runnable, priority);
			}
		}finally{
			lock.unlock();
		}
	}

	@Override
	public void cancelQueueByCategory(String category) {
		ArrayList<Runnable> keys = new ArrayList<Runnable>();
		ArrayList<Runnable> buffer = new ArrayList<Runnable>();
		lock.lock();
		try {
			q.drainTo(keys);
			for (Runnable cmd : keys) {
				if (((PriorityTask) cmd).category.equals(category)) {
					buffer.add(cmd);
				}
			}
			keys.removeAll(buffer);
			q.addAll(keys);
		} finally {
			lock.unlock();
		}
	}

	@Override
	public void cancelQueueByTaskID(int taskId) {
		lock.lock();
		try {
			Collection<IPriorityTask> ipts = taskManager.values();
			ArrayList<IPriorityTask> needClean = new ArrayList<IPriorityTask>();
			for (IPriorityTask task : ipts) {
				if (task.unregisterListener(taskId)) {
					needClean.add(task);
				}
			}
			// clean in Queue.
			if (needClean.size() > 0) {
				ArrayList<Runnable> keys = new ArrayList<Runnable>();
				ArrayList<Runnable> buffer = new ArrayList<Runnable>();
				q.drainTo(keys);
				PriorityTask pt;
				for (Runnable cmd : keys) {
					pt = (PriorityTask) cmd;
					if (needClean.contains(pt.runnable)) {
						buffer.add(cmd);
					}
				}
				keys.removeAll(buffer);
				q.addAll(keys);
			}
		} finally {
			lock.unlock();
		}
	}
}
